#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "sm4_crypto.h"

JNIEXPORT jint JNICALL Java_com_tianhe_common_tongsuo_SM4JNI_cbcEncrypt(JNIEnv *env,
                                              jobject obj,
                                              jbyteArray key,
                                              jbyteArray iv,
                                              jbyteArray plaintext,
                                              jbyteArray ciphertext)
{
    if (!key || !iv || !plaintext || !ciphertext) {
        fprintf(stderr, "Java_SM4JNI_cbcEncrypt: invalid parameters.\n");
        return (-1);
    }

    jbyte *pucKey = (*env)->GetByteArrayElements(env, key, NULL);
    jsize stKeyLen = (*env)->GetArrayLength(env, key);
    jbyte *pucIv = (*env)->GetByteArrayElements(env, iv, NULL);
    jsize stIvLen = (*env)->GetArrayLength(env, iv);
    jbyte *pucPlaintext = (*env)->GetByteArrayElements(env, plaintext, NULL);
    jsize stPlaintextLen = (*env)->GetArrayLength(env, plaintext);
    jbyte *pucCiphertext = (*env)->GetByteArrayElements(env, ciphertext, NULL);
    size_t stCiphertextLen = (*env)->GetArrayLength(env, ciphertext);
    jint iRet = -1;

    if (sm4CbcEncrypt((unsigned char *)pucKey, stKeyLen,
                      (unsigned char *)pucIv, stIvLen,
                      (unsigned char *)pucPlaintext, stPlaintextLen,
                      (unsigned char *)pucCiphertext, &stCiphertextLen) == -1) {
        fprintf(stderr, "Java_SM4JNI_cbcEncrypt: sm4CbcEncrypt failed.\n");
    } else {
        iRet = stCiphertextLen;
    }

    (*env)->ReleaseByteArrayElements(env, key, pucKey, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, iv, pucIv, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, plaintext, pucPlaintext, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, ciphertext, pucCiphertext, 0);

    return (iRet);
}

JNIEXPORT jint JNICALL Java_com_tianhe_common_tongsuo_SM4JNI_cbcDecrypt(JNIEnv *env,
                                              jobject obj,
                                              jbyteArray key,
                                              jbyteArray iv,
                                              jbyteArray ciphertext,
                                              jbyteArray plaintext)
{
    if (!key || !iv || !ciphertext || !plaintext) {
        fprintf(stderr, "Java_SM4JNI_cbcDecrypt: invalid parameters.\n");
        return (-1);
    }

    jbyte *pucKey = (*env)->GetByteArrayElements(env, key, NULL);
    jsize stKeyLen = (*env)->GetArrayLength(env, key);
    jbyte *pucIv = (*env)->GetByteArrayElements(env, iv, NULL);
    jsize stIvLen = (*env)->GetArrayLength(env, iv);
    jbyte *pucCiphertext = (*env)->GetByteArrayElements(env, ciphertext, NULL);
    jsize stCiphertextLen = (*env)->GetArrayLength(env, ciphertext);
    jbyte *pucPlaintext = (*env)->GetByteArrayElements(env, plaintext, NULL);
    size_t stPlaintextLen = (*env)->GetArrayLength(env, plaintext);
    jint iRet = -1;

    if (sm4CbcDecrypt((unsigned char *)pucKey, stKeyLen,
                      (unsigned char *)pucIv, stIvLen,
                      (unsigned char *)pucCiphertext, stCiphertextLen,
                      (unsigned char *)pucPlaintext, &stPlaintextLen) == -1) {
        fprintf(stderr, "Java_SM4JNI_cbcDecrypt: sm4CbcDecrypt failed.\n");
    } else {
        iRet = (jint)stPlaintextLen;
    }

    (*env)->ReleaseByteArrayElements(env, key, pucKey, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, iv, pucIv, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, ciphertext, pucCiphertext, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, plaintext, pucPlaintext, 0);

    return (iRet);
}

JNIEXPORT jint JNICALL Java_com_tianhe_common_tongsuo_SM4JNI_ecbEncrypt(JNIEnv *env,
                                              jobject obj,
                                              jbyteArray key,
                                              jbyteArray plaintext,
                                              jbyteArray ciphertext)
{
    if (!key || !plaintext || !ciphertext) {
        fprintf(stderr, "Java_SM4JNI_ecbEncrypt: invalid parameters.\n");
        return (-1);
    }

    jbyte *pucKey = (*env)->GetByteArrayElements(env, key, NULL);
    jsize stKeyLen = (*env)->GetArrayLength(env, key);
    jbyte *pucPlaintext = (*env)->GetByteArrayElements(env, plaintext, NULL);
    jsize stPlaintextLen = (*env)->GetArrayLength(env, plaintext);
    jbyte *pucCiphertext = (*env)->GetByteArrayElements(env, ciphertext, NULL);
    size_t stCiphertextLen = (*env)->GetArrayLength(env, ciphertext);
    jint iRet = -1;

    if (sm4EcbEncrypt((unsigned char *)pucKey, stKeyLen,
                      (unsigned char *)pucPlaintext, stPlaintextLen,
                      (unsigned char *)pucCiphertext, &stCiphertextLen) == -1) {
        fprintf(stderr, "Java_SM4JNI_ecbEncrypt: sm4EcbEncrypt failed.\n");
    } else {
        iRet = stCiphertextLen;
    }

    (*env)->ReleaseByteArrayElements(env, key, pucKey, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, plaintext, pucPlaintext, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, ciphertext, pucCiphertext, 0);

    return (iRet);
}

JNIEXPORT jint JNICALL Java_com_tianhe_common_tongsuo_SM4JNI_ecbDecrypt(JNIEnv *env,
                                              jobject obj,
                                              jbyteArray key,
                                              jbyteArray ciphertext,
                                              jbyteArray plaintext)
{
    if (!key || !ciphertext || !plaintext) {
        fprintf(stderr, "Java_SM4JNI_ecbDecrypt: invalid parameters.\n");
        return (-1);
    }

    jbyte *pucKey = (*env)->GetByteArrayElements(env, key, NULL);
    jsize stKeyLen = (*env)->GetArrayLength(env, key);
    jbyte *pucCiphertext = (*env)->GetByteArrayElements(env, ciphertext, NULL);
    jsize stCiphertextLen = (*env)->GetArrayLength(env, ciphertext);
    jbyte *pucPlaintext = (*env)->GetByteArrayElements(env, plaintext, NULL);
    size_t stPlaintextLen = (*env)->GetArrayLength(env, plaintext);
    jint iRet = -1;

    if (sm4EcbDecrypt((unsigned char *)pucKey, stKeyLen,
                      (unsigned char *)pucCiphertext, stCiphertextLen,
                      (unsigned char *)pucPlaintext, &stPlaintextLen) == -1) {
        fprintf(stderr, "Java_SM4JNI_ecbDecrypt: sm4EcbDecrypt failed.\n");
    } else {
        iRet = stPlaintextLen;
    }

    (*env)->ReleaseByteArrayElements(env, key, pucKey, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, ciphertext, pucCiphertext, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, plaintext, pucPlaintext, 0);

    return (iRet);
}